//
// Created by ASUS on 2023/9/20.
//
/*

给定一个按 非递减顺序 排列的数字数组 digits 。你可以用任意次数 digits[i] 来写的数字。例如，如果 digits = ['1','3','5']，我们可以写数字，如 '13', '551', 和 '1351315'。
返回 可以生成的小于或等于给定整数 n 的正整数的个数 。

示例 1：
输入：digits = ["1","3","5","7"], n = 100
输出：20
解释：
可写出的 20 个数字是：
1, 3, 5, 7, 11, 13, 15, 17, 31, 33, 35, 37, 51, 53, 55, 57, 71, 73, 75, 77.

示例 2：
输入：digits = ["1","4","9"], n = 1000000000
输出：29523
解释：
我们可以写 3 个一位数字，9 个两位数字，27 个三位数字，
81 个四位数字，243 个五位数字，729 个六位数字，
2187 个七位数字，6561 个八位数字和 19683 个九位数字。
总共，可以使用D中的数字写出 29523 个整数。

示例 3:
输入：digits = ["7"], n = 8
输出：1

提示：
1 <= digits.length <= 9
digits[i].length == 1
digits[i] 是从 '1' 到 '9' 的数
digits 中的所有值都 不同
digits 按 非递减顺序 排列
1 <= n <= 109
 */
#include <bits/stdc++.h>
using namespace std;
int atMostNGivenDigitSet(vector<string>& digits, int n) {
	auto s = to_string(n);
	int m = s.length(), dp[m];
	memset(dp, -1, sizeof(dp)); // dp[i] = -1 表示 i 这个状态还没被计算出来
	function<int(int, bool, bool)> f = [&](int i, bool is_limit, bool is_num) -> int {
		if (i == m) return is_num; // 如果填了数字，则为 1 种合法方案
		if (!is_limit && is_num && dp[i] >= 0) return dp[i]; // 在不受到任何约束的情况下，返回记录的结果，避免重复运算
		int res = 0;
		if (!is_num) // 前面不填数字，那么可以跳过当前数位，也不填数字
			// is_limit 改为 false，因为没有填数字，位数都比 n 要短，自然不会受到 n 的约束
			// is_num 仍然为 false，因为没有填任何数字
			res = f(i + 1, false, false);
		char up = is_limit ? s[i] : '9'; // 根据是否受到约束，决定可以填的数字的上限
		// 注意：对于一般的题目而言，如果这里 is_num 为 false，则必须从 1 开始枚举，由于本题 digits 没有 0，所以无需处理这种情况
		for (auto &d : digits) { // 枚举要填入的数字 d
			if (d[0] > up) break; // d 超过上限，由于 digits 是有序的，后面的 d 都会超过上限，故退出循环
			// is_limit：如果当前受到 n 的约束，且填的数字等于上限，那么后面仍然会受到 n 的约束
			// is_num 为 true，因为填了数字
			res += f(i + 1, is_limit && d[0] == up, true);
		}
		if (!is_limit && is_num) dp[i] = res; // 在不受到任何约束的情况下，记录结果
		return res;
	};
	return f(0, true, false);
}